﻿/*
 * Copyright 2011 Edward L. Stauff.                  Contact: <EdStauff@gmail.com>.
 * 
 * This file, "NiceTextReader.cs", is part of the "Stauffware.Common.IO" class library.
 * "Stauffware.Common.IO" is free software: you can redistribute it and/or 
 * modify it under the terms of the GNU Lesser Public License as published by the 
 * Free Software Foundation, either version 3 of the License, or (at your option) 
 * any later version.
 * 
 * "Stauffware.Common.IO" is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser Public License for more 
 * details.
 * 
 * You should have received a copy of the GNU Lesser Public License along with 
 * the "Stauffware.Common" sources.  If not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Stauffware.Common.Core;


		
		// NOT FINISHED YET!



namespace Stauffware.Common.IO
{
	/// <summary>
	/// A TextReader wrapper that provides some additional capabilities.
	/// </summary>
	public class NiceTextReader : TextReaderWrapper
	{
		/// <summary>
		/// Requests a new buffer size.
		/// </summary>
		/// <param name="currentSize">the current size of the buffer</param>
		/// <param name="additionalAmountNeeded">the additional amount needed</param>
		/// <returns>the new buffer size to allocate</returns>
		public delegate int BufferResizer (int currentSize, int additionalAmountNeeded);

		/// <summary>
		/// NiceTextReader calls this delegate if the unread buffer fills up and more
		/// space is needed.  If this delegate is null, or returns a value smaller
		/// than is required, then NiceTextReader throws an exception.
		/// </summary>
		public BufferResizer GetNewUnreadBufferSize;

//		public int LinesRead { get; }


		/// <see cref="TextReaderWrapper._InnerTextReader"/>
		protected override TextReader _InnerTextReader 
		{ get { return this._innerReader; } }
		
		private TextReader _innerReader;

		private char[] UnReadBuffer;
		private int unreadCount = 0;

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="innerReader">the TextReader from which data will be read</param>
		/// <param name="unReadBufferSize">
		///		the initial size of the buffer that stores unread characters before they're read again
		/// </param>

		public NiceTextReader (TextReader innerReader, int unReadBufferSize)
		{
			Require.NotNull(innerReader);
			this._innerReader = innerReader;
			this.UnReadBuffer = new char[unReadBufferSize];
		}

		//-----------------------------------------------------------------------------

		#region UnRead methods

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Pushes a character back into the input stream so that it will be
		/// re-read on subsequent Read calls. 
		/// </summary>
		
		public virtual void UnRead (char c)
		{
			this.CheckBufferSize(1);
			this.UnReadBuffer[this.unreadCount++] = c;
		}

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Pushes a string back into the input stream so that it will be
		/// re-read on subsequent Read calls.
		/// </summary>
		
		public virtual void UnRead (string s)
		{
			this.CheckBufferSize(s.Length);
			s.CopyTo(0, this.UnReadBuffer, this.unreadCount, s.Length);
			this.unreadCount += s.Length;
		}

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Pushes an array of characters back into the input stream so that the 
		/// characters will be re-read on subsequent Read calls. 
		/// </summary>
		
		public virtual void UnRead (char[] buffer, int index, int count)
		{
			Require.Condition(count >= 0);

			this.CheckBufferSize(count);
			Array.Copy(buffer, index, this.UnReadBuffer, this.unreadCount, count);
			this.unreadCount += count;
		}

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Pushes a line of text back into the input stream so that it will be
		/// re-read on subsequent Read calls.  An implicit newline is also unread.
		/// </summary>
		/// <param name="s"></param>

		public virtual void UnReadLine (string s)
		{
			this.CheckBufferSize(s.Length + 1);
			s.CopyTo(0, this.UnReadBuffer, this.unreadCount, s.Length);
			this.unreadCount += s.Length;
			this.UnReadBuffer[this.unreadCount++] = '\n';
		}

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Ensures that the unread buffer is large enough, resizing it if necessary.
		/// Throws an exception if it can't be resized.
		/// </summary>

		private void CheckBufferSize (int amountNeeded)
		{
			if (this.unreadCount + amountNeeded <= this.UnReadBuffer.Length)
				return;
			if (this.GetNewUnreadBufferSize != null)
			{
				int newSize = this.GetNewUnreadBufferSize(this.UnReadBuffer.Length,
									this.unreadCount + amountNeeded - this.UnReadBuffer.Length);
				if (newSize >= this.unreadCount + amountNeeded)
				{
					char[] oldBuf = this.UnReadBuffer;
					this.UnReadBuffer = new char[newSize];
					oldBuf.CopyTo(this.UnReadBuffer, 0);
					return;
				}
			}
			throw new IOException("unread buffer overflow");
		}

		//-----------------------------------------------------------------------------

		#endregion

		#region Read methods

		//-----------------------------------------------------------------------------
		/// <see cref="System.IO.TextReader.Read()"/>
		
		public override int Read ()
		{
			if (this.unreadCount > 0)
				return (int)this.UnReadBuffer[--this.unreadCount];
			else return base.Read();
		}

		//-----------------------------------------------------------------------------
		/// <see cref="System.IO.TextReader.ReadLine"/>
		
		public override string ReadLine ()
		{


			Assert.NotImplemented();
			return null;

	
		}

		//-----------------------------------------------------------------------------
		/// <see cref="System.IO.TextReader.ReadToEnd"/>
		
		public override string ReadToEnd ()
		{
			string s = new string(this.UnReadBuffer, 0, this.unreadCount);
			this.unreadCount = 0;
			return s + this._innerReader.ReadToEnd();
		}

		//-----------------------------------------------------------------------------
		/// <see cref="System.IO.TextReader.Read(char[],int,int)"/>
		
		public override int Read (char[] buffer, int index, int count)
		{
			Require.Condition(count >= 0);

			int n = Math.Min(this.unreadCount, count);
			this.unreadCount -= n;
			Array.Copy(this.UnReadBuffer, this.unreadCount, buffer, index, n);
			return n + this._innerReader.Read(buffer, index + n, count - n);
		}

		//-----------------------------------------------------------------------------
		/// <see cref="System.IO.TextReader.ReadBlock"/>
		
		public override int ReadBlock (char[] buffer, int index, int count)
		{
			return this.Read(buffer, index, count);
		}

		//-----------------------------------------------------------------------------
		// Reads up to max characters from the end of the unread buffer.

		private string ReadUnread (int max)
		{
			int n = Math.Min(max, this.unreadCount);
			if (n == 0)
				return "";
			this.unreadCount -= n;
			string s = new string(this.UnReadBuffer, this.unreadCount, n);
			return s;
		}

		//-----------------------------------------------------------------------------

		#endregion
	}
}
